console.log("生成器教学")

// 生成器，顾名思义，是生成“迭代器”的函数
// 生成器内部的yield返回的是各个状态
// 生成器返回的迭代器，每次调用next，都会返回yield后面的状态
// 生成的迭代器的迭代顺序就是生成器内部yield的代码顺序
function* mygenerator() {
    yield "status1";
    yield 200;
    yield { a: 100, b: "hello" };
    return 2.0;
}

// 由于 Generator 函数返回的遍历器对象，只有调用next方法才会遍历下一个内部状态，所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。
let t = mygenerator();

// 每次调用next都会返回下一个遍历器的结果对象，包含value和done两个属性，其中value就是yield的值，done是一个bool类型，为true就是整个生成器执行完毕，
// false就是整个生成器的语句没有执行完毕
console.log(t.next()); //{ value: 'status1', done: false }
console.log(t.next());//{ value: 200, done: false }
console.log(t.next().value.a);//100
console.log(t.next());//{ value: 2, done: true }

// 当然，我们熟悉迭代器的做法，for of遍历
// e=status1
// e=200
// e={ a: 100, b: 'hello' }
for (let e of mygenerator()) {
    console.log("e=%s", e);
}

// 如果没有return语句那么返回的值是undefined，但done仍然为true
mygenerator = function* () {
    yield 1;
    yield 2;
}

t = mygenerator();

console.log("no return yield 1,", t.next());//no return yield 1, { value: 1, done: false }
console.log("no return yield 2,", t.next());//no return yield 2, { value: 2, done: false }
console.log("no return yield 3,", t.next());//no return yield 3, { value: undefined, done: true }


// 这种提供暂停功能的做法就为异步执行提供了土壤，Generator 函数可以不用yield表达式，这时就变成了一个单纯的暂缓执行函数。
function* slowExec(a, b) {
    yield a + b;
}

t = slowExec(3, 5)
console.log(t.next().value);//相当于暂停一步，在任何想要的时候输出8即可

// generator函数的特点是初始化并不执行，需要next触发（首次没有yield也暂停，至少需要触发一次）
// 程序是执行完一个yield就暂停，调用next的时候，执行所有语句直到执行完一个yield再暂停

mygenerator = function* () {
    console.log("executed...");
}

t = mygenerator();
t.next();

// 由于yield表达式没有返回值，为了能得到返回值，可以给next方法带上一个参数，给了参数之后，yield表达式就有返回值了，比如：
function* returnValueOfGenerator() {
    var a = yield 100;
    var b = yield 200 * a;
    return a + b;
}

t = returnValueOfGenerator();
console.log(t.next(2)); //{ value: 100, done: false }  
console.log(t.next(2)); //{ value: 400, done: false } 
console.log(t.next(3)); //{ value: 4, done: true }

// 这个功能有很重要的语法意义。Generator 函数从暂停状态到恢复运行，它的上下文状态（context）是不变的。
// 通过next方法的参数，就有办法在 Generator 函数开始运行之后，继续向函数体内部注入值。
// 也就是说，可以在 Generator 函数运行的不同阶段，从外部向内部注入不同的值，从而调整函数行为

// 深度理解yield
function* deepInYield() {
    console.log('Hello' + (yield)); // { value: undefined, done: false }
    console.log('Hello' + (yield 123)); // OK
}

t = deepInYield();
console.log(t.next()); // { value: undefined, done: false }
console.log(t.next()); // Helloundefined
console.log(t.next()); // { value: 123, done: false }  

// for of 循环可以自动调用iterator
function* testForOf() {
    yield 1;
    yield 2;
    yield 3;
    yield 4;
}

t = testForOf();

for (let e of t) {
    console.info(e);
}

// throw()
// 这个方法相当于在外部抛出异常，函数在内部执行，却在外部抛出异常
// 这个外部的异常，可以被内部try catch 捕获
t = testForOf();
console.log(t.next()); // 1, false
// t.throw("outter errors"); // 这里抛出了错误，程序无法执行了

// 修改生成器函数
function* testForOf2() {
    try {
        yield 1;
        yield 2;
        yield 3;
        yield 4;
    } catch (e) {
        console.error(e);
    } finally {
        console.log("finally exec");
    }
}

t = testForOf2();
console.log(t.next()); // 1, false
t.throw(new Error("outter errors")); // 这里抛出了错误，被生成器内部的try捕捉

// 生成器套用生成器
// 使用yield*来调用另外的生成器
function* outterGener() {
    yield 1;
    yield* innerGener();
    yield 4;
}

function* innerGener() {
    yield 2;
    yield 3;
}

t = outterGener();

for (let e of t) {
    console.log(e);
}

// 生成器始终返回的是遍历器对象，而不是this，比如：
function* tryReturnThisButNotWork() {
    yield 1;
    yield 2;
    return 100;
}

t = tryReturnThisButNotWork();
console.log(t); // 返回的是Object [Generator] {}，而不是100

// t = new tryReturnThisButNotWork();  //TypeError: tryReturnThisButNotWork is not a constructor这里无法实例化
